---------------------------------------------------------------
File name   :  vv_ahblite_monitor.e
Title       :  Bus and agent monitors implementation.
Project     :  vv_ahblite eVC
Developers  :  stefan,filip
Created     :  29.07.2011.
Description :  This file implements the bus monitor and agent monitor units.
Notes       :
---------------------------------------------------------------
Copyright  (c)2011
---------------------------------------------------------------

<'
package vv_ahblite;

-- ==========================================================================
-- Bus monitor unit implementation
-- ==========================================================================
	


extend vv_ahblite_bus_monitor {
	
	
	--@@@@
    --event decode_address is activated when there is achange of address signals
    event decode_address is change(p_smp.h_addr$) @sim;

    -- when the address changes decode it and activate the apropriate HSEL signal
    on decode_address {
    	 --decode address
        var slave: vv_ahblite_slave;             
        message(LOW, "\n \n DECODE ADDRESS ",p_smp.h_addr.get_mvl());
        
        if(p_smp.has_default_slave){
        		p_smp.AHB_DEFAULT_SLAVE_HSEL$ = 0;
        	};
        for each in p_env.slaves {

        	it.h_sel$ = 0;        	
        };     
        slave = p_env.slaves.first(.in_range(p_smp.h_addr$));
        
        if (slave == NULL){    	
        	--access to a nonexistent address location
        	--flag = FALSE;
        	if(p_smp.has_default_slave){
        		p_smp.AHB_DEFAULT_SLAVE_HSEL$ = 1;
        		start default_slave();
        	};
        }else{
        	--activate the apropriate HSEL signal
        	slave.h_sel$  = 1;  
        	--flag = TRUE;
        	-----
        	--start change_slave(slave);
        	-----
        };
    };
    
    -- dafault_slave is activated when there is a transaction to a nonexistent address
    default_slave()@p_sys_smp.clk is{
    	-- response when accessing a nonexistant address location
    	message(LOW, "\n \n *** DEFAULT SLAVE!! ");
    	--if(!(p_smp.h_ready$ == 0)){
    		
	    	if (p_smp.get_h_trans() in [IDLE,BUSY]){
	    		wait cycle;
	    		sync true (p_smp.h_ready$ == 1);
	    		-- if there is an IDLE or BUSY transfer, default slave responds with zero wait state OK transfer     		
	    		message(LOW, "\n \n *** DEFAULT SLAVE - idle or busy response!! ");
	    		p_smp.AHB_HRESP$ = 0;
	    		p_smp.AHB_HREADY$ = 1;
	    		p_smp.AHB_HRDATA$ = 0; 		
	    		--p_smp.h_resp$ = 0;
	        	--p_smp.h_ready$ = 1;
	        }else{
	        -- error response
        		wait cycle;
        		sync true (p_smp.h_ready$ == 1);
        		p_smp.AHB_HRESP$ = 1;
        		p_smp.AHB_HREADY$ = 0;
        		p_smp.AHB_HRDATA$ = 0;
        		
        		--p_smp.h_resp$ = 1;
        		--p_smp.h_ready$ = 0;        		
        		wait cycle;   
        		p_smp.AHB_HRESP$ = 1;
        		p_smp.AHB_HREADY$ = 1;
        		p_smp.AHB_HRDATA$ = 0;    		
        		--p_smp.h_resp$ = 1;
        		--p_smp.h_ready$ = 1;
	        };
    	--};
    };
	
    main_loop() @p_sys_smp.clk is {
        current_master = p_env.master;
        while TRUE {
            transaction_delay_start = p_sys_smp.current_cycle;
            -- put a condition that will determine the correct moment for collecting the transaction
            
            // ==============
            if (last_transfer != NULL and  (transaction.transfer == NONSEQ or transaction.transfer == IDLE ) ) {
            	busy_num = 0;
            	wait_states_num = 0;
            };
            if (previous_transaction != NULL and transaction.ready == 1)
            {
            	last_transfer = transaction;
            };
            previous_transaction = transaction;
            // =============
            
            collect_transaction();
            
            // ===================
            if (previous_transaction != NULL and previous_transaction.ready == 0){ 
            	if (previous_transaction.transfer != transaction.transfer and previous_transaction.ready == 0){
            		emit trans_change;
            	};
            };
            
            if (previous_transaction != NULL and transaction.ready == 1) 
            { --burst_end emitted only if we are not in wait state
            	if(transaction.transfer == BUSY and in_burst)
            	{	
            		busy_num += 1;
            	};
            	
            	if (last_transfer!=NULL)
            	{
            		emit transfer_end; 
            		message(HIGH, " # # # emitted transfer end for address: ", last_transfer.addr); 
            	};

	            if (last_transfer != NULL and  transaction.transfer == NONSEQ ) 
	            { 
	            	if (idle_emitted == FALSE ) 
	            		{
	            			
	            		emit burst_end; 
	            		message(HIGH, " % % % NONSEQ emitted burst_end ", last_transfer.transaction_str()); 
	            		message(HIGH, " % % % wait_states: ", wait_states_num); 

	            			            		
	            		};
	            	idle_emitted = FALSE;
	            	in_burst = TRUE;
	          	}
	            else if (last_transfer != NULL and  transaction.transfer == IDLE )
	            {
	            		if ( previous_transaction.transfer!= IDLE ) 
	            		{ 
	            			emit burst_end; 
	            			message(HIGH, "% % % IDLE emitted burst_end ", last_transfer.transaction_str());
	            			message(HIGH, " % % % wait_states: ", wait_states_num); 
	            			
	            			in_burst = FALSE;
	            			idle_emitted = TRUE;	            			
	            		};	            		            	
	            }; 
	            
            }
            else if (transaction.ready == 0 and transaction.response == 0 and in_burst == TRUE)
	        {
	            	wait_states_num = wait_states_num + 1;             	
	        };        
	        // =================
	        
	        wait cycle;
        };
    };
    
    

    collect_transaction() @p_sys_smp.clk is { --it will collect transaction in every cycle

        -- Initialize the data item
        transaction = new MONITOR vv_ahblite_transaction with {
            --master signals
            .transaction_kind = MONITOR;
            .addr = p_smp.h_addr$;
            .write_op = p_smp.h_write$;
            .write_data = pack(packing.low, p_smp.h_wdata$);--@@@@ 
            .burst = p_smp.get_h_burst();
            .lock = p_smp.h_mastlock$;
            .protect = p_smp.h_prot$;
            .data_size = p_smp.get_h_size();
            .transfer = p_smp.get_h_trans();
            
            --slave signals
            .read_data = pack(packing.low, p_smp.h_rdata$); --pack bits into list of bytes
            .ready = p_smp.h_ready$;
            .response = p_smp.h_resp$;
          };
        --@@@@ what if there is no slave in range???
        current_slave = p_env.slaves.first(.in_range(transaction.addr));
        
        message(HIGH, "\n\n[monitor]>> Detected transaction start :", transaction.transaction_str());

        transaction.start_transaction();
        emit transaction_start;

        -- Update the master name and slave name fields
        transaction.master_name = current_master.name;
        if(current_slave == NULL) then {
        	-- if there is no slave in range
        	transaction.slave_name = DEFAULT_SLAVE;
        }else {
        	transaction.slave_name = current_slave.name;
        };

        -- coverage events
        message(HIGH, "Detected transaction end :", transaction.transaction_str());
        transaction.end_transaction();      
        emit transaction_end;

        -- ******************************************************************
    };
    
   
    -- This method can be extended to initiate the bus monitor unit.
    -- It is called each time run() is called.
    init_values() is {
    	    	
    	p_smp.h_sel_s0$ = 0;
    	p_smp.h_sel_s1$ = 0;
    	p_smp.h_sel_s2$ = 0;
    	p_smp.h_sel_s3$ = 0;
    	
    	idle_emitted = FALSE;
    	in_burst = FALSE;
    	wait_states_num = 0;
    	busy_num = 0;
    };
    
    run() is also {
        message(LOW, "Bus monitor started");
        init_values();
        start main_loop();
    }; 
      
   -- This method returns the current status of the bus monitor.
    show_status() is {
        message(LOW, "Bus monitor detected ", dec(num_transactions), " transactions");
    }; 
    
    -- Performs processing needed at the end of each transaction
    on transaction_end { 
        num_transactions += 1;
        -- Ensure that each transaction is written to the log file.
        message(VV_AHBLITE_FILE, MEDIUM,"[",sys.time,"] ",transaction.transaction_str());
        -- A call to the scoreboard hookm method port
        transaction_complete$(transaction); 
    };
 
}; 
'>
<'
-- ==========================================================================
-- Agent monitor unit implementation.
-- ==========================================================================
extend vv_ahblite_agent_monitor {

    run() is also {
        message(LOW, "Agent monitor started");
        init_values();
    }; 
    
    -- This is an internal event emitted when this agent monitor detects 
    -- the end of a relevant transaction.
    event agent_transaction_end;
    
    event agent_burst_end;  --used for coverage
    event agent_transfer_end;
    event agent_trans_change;
    
    -- On agent_transaction end, update the transaction field and 
    -- activate the scoreboard hook. Then ensure that the transaction 
    -- is written to the log file.
    on agent_transaction_end {
    	message(HIGH,"!!! AGENT_TRANSACTION_END: ", p_bus_monitor.transaction.transaction_str());
        transaction = p_bus_monitor.transaction;
        emit transaction_end;
        transaction_complete$(transaction); --call the scoreboard hook
        num_transactions += 1;
        message(VV_AHBLITE_FILE, MEDIUM,"[",sys.time,"] ",transaction.transaction_str());
    };
    
    on agent_burst_end {
    	message(HIGH,"!!! AGENT_BURST_END: ", p_bus_monitor.last_transfer.transaction_str());
        last_transfer = p_bus_monitor.last_transfer;
        wait_states_num = p_bus_monitor.wait_states_num;
        busy_num = p_bus_monitor.busy_num;

        message(HIGH,"!!! BURST END wait_states: ", wait_states_num);
        
        emit burst_end;
        --message(VV_AHBLITE_FILE, MEDIUM,"[",sys.time,"] ",transaction.transaction_str());
    };
    on agent_transfer_end {
        last_transfer = p_bus_monitor.last_transfer;
        emit transfer_end;
        --message(VV_AHBLITE_FILE, MEDIUM,"[",sys.time,"] ",transaction.transaction_str());
    };
    on agent_trans_change {
    	transaction= p_bus_monitor.transaction;
    	previous_transaction =  p_bus_monitor.previous_transaction;
    	emit trans_change;
    };
};

-- ==========================================================================
-- The master monitor unit implementation.
-- ==========================================================================
extend vv_ahblite_master_monitor {

    -- This event is emitted when the bus monitor detects a transaction
    -- and the monitor's agent is the master responsible for that
    -- item.
    event agent_transaction_end is only 
        true(p_bus_monitor.current_master == p_agent) @p_bus_monitor.transaction_end;
    --  This event is emitted when the bus monitor detects a burst end
    -- and the monitor's agent is the master responsible for that
    -- item.
    event agent_burst_end is only 
        true(p_bus_monitor.current_master == p_agent) @p_bus_monitor.burst_end;
    --  This event is emitted when the bus monitor detects a transfer end
    -- and the monitor's agent is the master responsible for that
    -- item.
    event agent_transfer_end is only 
        true(p_bus_monitor.current_master == p_agent) @p_bus_monitor.transfer_end;
    --  This event is emitted when the bus monitor detects a transfer change
    -- and the monitor's agent is the master responsible for that
    -- item.
    event agent_trans_change is only
    	true(p_bus_monitor.current_master == p_agent) @p_bus_monitor.trans_change;
        
        
    -- This method reports the current status of the master
    -- monitor.
    show_status() is {
        message(LOW, "The master monitor detected ", dec(num_transactions), " transactions");
};

}; 

-- ==========================================================================
-- The slave monitor unit implementation.
-- ==========================================================================
extend vv_ahblite_slave_monitor {

    -- This event is emitted when the bus monitor detects a transaction 
    -- and the monitor's agent is the slave addressed by the item.
    event agent_transaction_end is only
        true(p_bus_monitor.current_slave == p_agent) @p_bus_monitor.transaction_end;
        
    event agent_burst_end is only 
        true(p_bus_monitor.current_slave == p_agent) @p_bus_monitor.burst_end;
        
    event agent_transfer_end is only 
        true(p_bus_monitor.current_slave == p_agent) @p_bus_monitor.transfer_end;
        
    event agent_trans_change is only
    	true(p_bus_monitor.current_slave == p_agent) @p_bus_monitor.trans_change;
    
    -- This method reports the current status of the slave monitor
    show_status() is {
        message(LOW, "The slave monitor detected ", dec(num_transactions), " transactions");
};

}; 
'>
